﻿namespace JumpJump.Runtime
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// This class used for caculate the chessboard information
    /// </summary>
    public class Chessboard
    {
        #region Fields

        /// <summary>
        /// the board height
        /// </summary>
        public const int ChessboardHeight = 18;

        /// <summary>
        /// the board width
        /// </summary>
        public const int ChessboardWidth = 18;

        /// <summary>
        /// this stand for where can never put pieces
        /// </summary>
        public const int IllegalPosition = -1;

        /// <summary>
        /// this stand for where is empty
        /// </summary>
        public const int EmptyPosition = 0;

        /// <summary>
        /// Piece number for each player
        /// </summary>
        public const int PlayerPieceCount = 10;

        /// <summary>
        /// User ID
        /// </summary>
        public const int UserID = 1;

        /// <summary>
        /// Prick ID
        /// </summary>
        public const int PrickID = 2;

        /// <summary>
        /// all player, both AI and user
        /// </summary>
        public const int AllPlayer = 3;

        /// <summary>
        /// User Field
        /// </summary>
        public int[,] InitialUserPiecePosition = new int[10, 2]
        {
            { 5, 1 },
            { 5, 2 },
            { 6, 2 },
            { 5, 3 },
            { 6, 3 },
            { 7, 3 },
            { 5, 4 },
            { 6, 4 },
            { 7, 4 },
            { 8, 4 },
        };

        /// <summary>
        /// AI field
        /// </summary>
        public int[,] InitialPrickPiecePosition = new int[10, 2]
        {
            { 13, 17 },
            { 12, 16 },
            { 13, 16 },
            { 11, 15 },
            { 12, 15 },
            { 13, 15 },
            { 10, 14 },
            { 11, 14 },
            { 12, 14 },
            { 13, 14 },
        };

        /// <summary>
        /// use matrix to save the current status
        /// </summary>
        private int[,] _board = new int[ChessboardWidth, ChessboardHeight];

        /// <summary>
        /// store user pieces
        /// </summary>
        private List<Piece> _userPieces = new List<Piece>();

        /// <summary>
        /// store AI pieces
        /// </summary>
        private List<Piece> _prickPieces = new List<Piece>();

        /// <summary>
        /// replay to save this game information
        /// </summary>
        private Replay _replay; 

        #endregion

        #region Construstor

        /// <summary>
        /// Initializes a new instance of the Chessboard class
        /// </summary>
        public Chessboard()
        {
            this.InitBoard();
            this.InitPlayerPieces();
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets board statue
        /// </summary>
        public int[,] Board
        {
            get
            {
                return this._board;
            }
        }

        /// <summary>
        /// Gets User picecs list
        /// </summary>
        public IList<Piece> UserPieces
        {
            get
            {
                return this._userPieces;
            }
        }

        /// <summary>
        /// Gets and Sets AI pieces
        /// </summary>
        public IList<Piece> PrickPieces
        {
            get
            {
                return this._prickPieces;
            }
        }

        /// <summary>
        /// Gets Replay
        /// </summary>
        public Replay Replay
        {
            get
            {
                return this._replay;
            }
            set
            {
                this._replay = value;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Move one piece
        /// </summary>
        /// <param name="piece">piece ready to move</param>
        /// <param name="target">target position</param>
        public void PieceMove(Piece piece, Coordinate target)
        {
            int fromX = piece.Position.X;
            int fromY = piece.Position.Y;
            piece.MoveTo(target);
            this._board[target.X, target.Y] = piece.PlayerID;
            this._board[fromX, fromY] = EmptyPosition;
            this._replay.InsertLastMove(new Movement
                (piece.PlayerID, piece.PieceID, piece.Position, target));
        }

        /// <summary>
        /// move one piece
        /// </summary>
        /// <param name="uid">user ID</param>
        /// <param name="pid">piece ID</param>
        /// <param name="target">target coordnate</param>
        public void PieceMove(int uid, int pid, Coordinate target)
        {
            Piece piece;
            if (uid == Chessboard.UserID)
            {
                piece = this.UserPieces[pid];
            }
            else
            {
                piece = this.PrickPieces[pid];
            }

            PieceMove(piece, target);
        }

        /// <summary>
        /// work around Move one piece
        /// </summary>
        /// <param name="piece">piece ready to move</param>
        /// <param name="target">target position</param>
        public void PieceMoveWithoutInsertToRep(Piece piece, Coordinate target)
        {
            int fromX = piece.Position.X;
            int fromY = piece.Position.Y;
            piece.MoveTo(target);
            this._board[fromX, fromY] = EmptyPosition;
            this._board[target.X, target.Y] = piece.PlayerID;
        }

        /// <summary>
        /// Creat a initial board array
        /// </summary>
        /// <returns>initial board value</returns>
        public static int[,] InitialBoard()
        {
            Chessboard board = new Chessboard();
            int[,] initial = board._board;
            return initial;
        }

        /// <summary>
        /// get pid and uid from the coordinate
        /// </summary>
        /// <param name="x">x coordinate</param>
        /// <param name="y">y coordinate</param>
        /// <param name="pID">piece id</param>
        /// <param name="uID">user id</param>
        public void GetPicecInfoFromCordinate(int x, int y, ref int pID, ref int uID)
        {
            uID = _board[x, y];
            if (uID == PrickID)
            {
                foreach (Piece p in _prickPieces)
                {
                    if (p.Position.IsSameCoordinate(x, y))
                    {
                        pID = p.PieceID;
                    }
                }
            }
            else
            {
                foreach (Piece p in _userPieces)
                {
                    if (p.Position.IsSameCoordinate(x, y))
                    {
                        pID = p.PieceID;
                    }
                }
            }
        }

        /// <summary>
        /// Make a new copy of chess board after use next move
        /// </summary>
        /// <param name="currentBoard">current chessboard</param>
        /// <param name="nextMove">next move</param>
        /// <returns>new chess board after move</returns>
        public static Chessboard CreatNewBoard(Chessboard currentBoard, Movement nextMove)
        {
            return null;
        }

        /// <summary>
        /// Caculate all the valid movement of all piece in current board
        /// </summary>
        public void GetAllPlayersAllValidMovement()
        {
            foreach (Piece onePiece in this.UserPieces)
            {
                onePiece.GetNextPositions(this.Board);
            }

            foreach (Piece onePiece in this.PrickPieces)
            {
                onePiece.GetNextPositions(this.Board);
            }
        }

        /// <summary>
        /// Get A list that contains all valid movement for a given player, user or AI
        /// </summary>
        /// <param name="playerID">user or AI</param>
        /// <returns>all valid movement</returns>
        public List<Movement> GetOnePlayerAllValidMovement(int playerID)
        {
            List<Movement> allMovement = new List<Movement>();
            if (playerID == Chessboard.UserID)
            {
                foreach (Piece onePiece in this.UserPieces)
                {
                    onePiece.GetNextPositions(this.Board);

                    foreach (Coordinate coor in onePiece.CanMoveToPositionList)
                    {
                        Movement oneMove = new Movement(onePiece.PlayerID, onePiece.PieceID, onePiece.Position, coor);
                        allMovement.Add(oneMove);
                    }
                }
            }
            else
            {
                foreach (Piece onePiece in this.PrickPieces)
                {
                    onePiece.GetNextPositions(this.Board);

                    foreach (Coordinate coor in onePiece.CanMoveToPositionList)
                    {
                        Movement oneMove = new Movement(onePiece.PlayerID, onePiece.PieceID, onePiece.Position, coor);
                        allMovement.Add(oneMove);
                    }
                }
            }

            return allMovement;
        }

        /// <summary>
        /// check which player is win
        /// </summary>
        /// <returns>winner or no one win</returns>
        public int WinnerCheck()
        {
            int nonWinner = 0;
            bool prickWinFlag = true;
            for (int i = 0; i < PlayerPieceCount; i++)
            {
                if (this._board[this.InitialUserPiecePosition[i, 0], this.InitialUserPiecePosition[i, 1]] == PrickID)
                {
                    continue;
                }
                else
                {
                    prickWinFlag = false;
                    break;
                }
            }

            if (prickWinFlag)
            {
                return PrickID;
            }

            bool playerWinFlag = false;
            for (int i = 0; i < PlayerPieceCount; i++)
            {
                if (this._board[this.InitialPrickPiecePosition[i, 0], this.InitialPrickPiecePosition[i, 1]] == UserID)
                {
                    continue;
                }
                else
                {
                    playerWinFlag = false;
                    break;
                }
            }

            if (playerWinFlag)
            {
                return UserID;
            }

            return nonWinner;
        }

        /// <summary>
        /// Insert last movement to replay
        /// </summary>
        /// <param name="move"></param>
        public void InsertLastMoveToReplay(Movement move)
        {
            
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// set initial value of each position
        /// </summary>
        private void InitBoard()
        {
            for (int i = 0; i < ChessboardHeight; i++)
            {
                for (int j = 0; j < ChessboardWidth; j++)
                {
                    this._board[i, j] = IllegalPosition;
                }
            }

            for (int i = 1; i < ChessboardWidth; i++)
            {
                int up = Validator.CoordinateRange[i, 0];
                int down = Validator.CoordinateRange[i, 1];

                for (int j = up; j <= down; j++)
                {
                    this._board[i, j] = EmptyPosition;
                }
            }
        }

        /// <summary>
        /// set initial values of each pieces
        /// </summary>
        private void InitPlayerPieces()
        {
            for (int i = 0; i < PlayerPieceCount; i++)
            {
                int x1 = this.InitialUserPiecePosition[i, 0];
                int y1 = this.InitialUserPiecePosition[i, 1];
                this._board[x1, y1] = UserID;
                Piece userPiece = new Piece(UserID, i, x1, y1);
                this._userPieces.Add(userPiece);

                int x2 = this.InitialPrickPiecePosition[i, 0];
                int y2 = this.InitialPrickPiecePosition[i, 1];
                this._board[x2, y2] = PrickID;
                Piece prickPiece = new Piece(PrickID, i, x2, y2);
                this._prickPieces.Add(prickPiece);
            }
        }


        #endregion 
    }
}
